#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string>
#include<cstring>
#include<cstdlib>//等同于 stdlib.h
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

#define N 2
#define NUM 1024

//子进程通信方法
void Writer(int wfd)
{
    string s = "hello fx,I am child process";
    pid_t self = getpid();
    int number = 0 ;
    //目标：把上面三个信息打包，交给父进程
    char buffer[NUM];//模拟缓冲区 - 要注意，传输时数据都是被转成 字符 操作的,缓冲区是做这个工作的
    while(true)
    {
        // 1. 构建发送字符串
        buffer[0] = 0;//这是做什么? -> 字符串清空,为了提醒阅读代码的人，把这个数组当成字符串
        //snprintf : c/c++混编接口
        snprintf(buffer,sizeof(buffer),"%s-%d-%d\n",s.c_str(),self,number);
        number++;
        //cout<< buffer<<endl;//显示，看是否写入成功

        // 2. 发送给父进程--pipe我们得到了文件描述符
        //一切皆文件：那么无论这个文件是不是“真实”存在，我们都可以以文件方式写入(与往磁盘写入相同)
        write(wfd,buffer,strlen(buffer));
        // cout<<number<<endl;
        sleep(1);

        // 3. 验证管道多大 - 一次写一个字节
        // char c = 'c';
        // write(wfd,&c,1);
        // cout<<number++<<endl;

        // 4. 读端一直读，写端关闭会怎么样?
        // char c = 'c';
        // write(wfd,&c,1);
        // cout<<number++<<endl;

        // if(number > 5) break;
    }
}

//父进程通信方法
void Reader(int rfd)
{
    char buffer[NUM];

    // 5. 写端一直写，读端关闭：OS会杀掉写端进程
    int cnt =0;
    while(true)//持续读取
    {
        //先让子进程写，后读呢?
        // sleep(5);

        // sleep(50);//验证管道多大 - ulimit -a 可查看(显示512字节，当成PIPE_BUF大小即可 - 但测试发现为64)


        buffer[0] = 0;//把数组当成字符串
        ssize_t n = read(rfd,buffer,sizeof(buffer));//sizeof!=strlen
        if(n>0)//返回值为实际读取到的数据长度
        {
            buffer[n] = '\0';//0 == '\0' 把数据当成字符串用(文件内容不当成c那套字符串)
            cout<<"parent get message["<<getpid()<<"]# "<<buffer<<endl;

        }
        else{
            cout<<"parent read file done!"<<endl;
            break;
        }
        // cout<<"n:"<<n<<endl;//返回值 n == 0说明读到文件结尾了(不会阻塞)
        sleep(1);
        cnt++;
        if(cnt > 5) break;
    }
}


int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd);//父进程建立管道: 打开两个文件，读端/写端
    if(n<0) return 1;

    // cout<<"pipefd[0]:"<<pipefd[0]<<" pipefd[1]:"<<pipefd[1]<<endl;

    //形成单向信道：
    //规定 child: w ; 关闭pipefd[0]
    //     parent: r; 关闭pipefd[1]
    pid_t id = fork();
    if(id < 0) return 2;
    else if(id == 0)
    {
        //child
        close(pipefd[0]);
        //IPC code
        Writer(pipefd[1]);


        close(pipefd[1]);
        exit(0);
    }
    //parent
    close(pipefd[1]);
    //IPC code
    Reader(pipefd[0]);

    close(pipefd[0]);
    cout<<"parent close read fd:"<<pipefd[0] <<endl;
    sleep(5);//观察子进程的僵尸状态--读端关闭，子进程就被终止了

    int status = 0;
    pid_t rid = waitpid(id,&status,0);//等待进程结束
    if(rid < 0) return 3;
    
    cout<<"wait success: "<<rid<<" exit code: "<<((status>>8)&0xFF)<<" status signal: "<<(status&0x7F)<<endl;
    sleep(3);
    cout<<"parent quit"<<endl;
    return 0;
}


